﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ManagedNite;
using System.Diagnostics;
using System.Threading;

namespace MediaPortal.Plugins.Sense
{
    public class SensorHandler
    {
        protected XnMSessionManager sessionManager;
        protected XnMOpenNIContext context;
        protected XnMFlowRouter startRouter;
        protected XnMSteadyDetector steadyDetector;
        protected NavigatorControl menuNavigator;

        protected bool online = false;
        protected bool listenForEndSession = false;

        protected Thread sensorThread;
        
        #region Events

        public event Action<SensorEvent, bool> Action;

        #endregion

        #region Public methods

        public virtual bool Start()
        {
            if (!this.online)
            {
                this.online = true;

                Logger.Info("Starting sensor.");

                if (sensorThread == null)
                {
                    sensorThread = new Thread(Run);
                    sensorThread.IsBackground = true;
                    sensorThread.Name = "SensorThread";
                    sensorThread.Start();
                }
                
                return true;
            }

            return false;
        }

        public virtual bool Stop()
        {
            if (this.online)
            {
                Logger.Info("Stopping sensor.");
                this.online = false;
                sensorThread.Join(10000);
                return true;
            }

            return false;
        }

        #endregion

        #region Threadworker

        protected void Run()
        {
            try
            {
                Logger.Info("Initializing sensor.");
                this.context = new XnMOpenNIContext();
                this.context.Init();

                this.sessionManager = new XnMSessionManager(context, "Wave", "RaiseHand");
                this.sessionManager.FocusStartDetected += new EventHandler<FocusStartEventArgs>(OnFocusStartDetected);
                this.sessionManager.SessionStarted += new EventHandler<PointEventArgs>(OnSessionStarted);
                this.sessionManager.SessionEnded += new EventHandler(OnSessionEnded);
                this.sessionManager.QuickRefocusTimeout = 10000;

                this.steadyDetector = new XnMSteadyDetector();
                this.steadyDetector.Steady += new EventHandler<SteadyEventArgs>(OnStartSteady);
                
                this.menuNavigator = new NavigatorControl();
                this.menuNavigator.Action += new Action<SensorEvent>(OnMenuNavigatorAction);

                this.startRouter = new XnMFlowRouter();
                this.startRouter.SetActiveControl(steadyDetector);

                this.sessionManager.AddListener(startRouter);

                Logger.Info("Sensor initialized and online.");

                while (this.online)
                {
                    uint rc = this.context.Update();
                    if (rc == 0)
                    {
                        this.sessionManager.Update(context);
                    }
     
                }
            }
            catch (Exception e)
            {
                Logger.Error("Sensor encountered and error.", e.Message);
                this.online = false;
                return;
            }
            finally
            {
                if (this.context != null)
                {
                    this.context.Close();
                    this.context.Dispose();
                }     

                if (this.sessionManager != null)
                {
                    this.sessionManager.RemoveListener(startRouter);
                    this.sessionManager.ClearQueue();
                    this.sessionManager = null;
                }
                
            }

            Logger.Info("Sensor offline.");
        }

        #endregion

        #region Session Event Handlers

        protected void OnFocusStartDetected(object sender, FocusStartEventArgs e)
        {
            Logger.Debug("OnFocusStartDetected.");
        }
        
        protected void OnSessionStarted(object sender, PointEventArgs e)
        {
            XnMSessionManager session = sender as XnMSessionManager;
            Logger.Info("Session started.");
            RaiseSensorEvent(SensorEvent.SessionStart);
        }

        protected void OnSessionEnded(object sender, EventArgs e)
        {
            Logger.Info("Session ended.");
            RaiseSensorEvent(SensorEvent.SessionEnd);
        }

        #endregion

        #region Internal event handlers

        protected void OnStartSteady(object sender, SteadyEventArgs e)
        {
            Logger.Debug("Start navigation (steady)");
            this.startRouter.ClearQueue();
            this.startRouter.SetActiveControl(this.menuNavigator);
        }

        protected void OnMenuNavigatorAction(SensorEvent e)
        {
            RaiseSensorEvent(e);
        }

        #endregion

        #region External events

        protected void RaiseSensorEvent(SensorEvent control)
        {
            RaiseSensorEvent(control, false);
        }

        protected void RaiseSensorEvent(SensorEvent control, bool repeat)
        {
            Logger.Debug("Raise: SensorEvent: {0} Repeat: {1}", control, repeat);

            if (listenForEndSession)
            {
                Logger.Debug("Cancelled end session gesture.");
                listenForEndSession = false;
                repeat = false;
            }

            if (Action != null)
            {
                Action(control, repeat);
            }
        }

        #endregion
    }
}
